#include <QDebug>
#include <QUrlQuery>
#include <QJsonDocument>
#include <QJsonArray>
#include "server.h"

Server::ClientData::ClientData()
{
    ollamaReply = nullptr;    
    ollamaRecv = 0;
    session = -1;
    chatId = -1;
}

void Server::ClientData::clear()
{
    session = -1;
    chatId = -1;    
    closeOllamaReply();
}

void Server::ClientData::closeOllamaReply()
{
    if(ollamaReply)
    {
        ollamaReply->close();
        ollamaReply = nullptr;        
    }
    ollamaRecv = 0;    
}

//Server
Server::Server()
{
    m_manager = new QNetworkAccessManager(this);
    connect(&m_server, &JZRpcServer::sigNewConnect, this, &Server::onNetConnect);
    connect(&m_server, &JZRpcServer::sigNetPackRecv, this, &Server::onNetRecv);
    connect(&m_server, &JZRpcServer::sigDisConnect, this,  &Server::onNetDisconnect);
}

Server::~Server()
{
    m_server.stopServer();
}

bool Server::start(int port)
{
    return m_server.startServer(port);
}

bool Server::isValidCliend(int net_id)
{
    return m_clients.contains(net_id);
}

void Server::onNetConnect(int id)
{
    m_dataManager.newSession(id, nullptr);    
    m_clients[id] = QSharedPointer<ClientData>(new ClientData());
}

void Server::onNetDisconnect(int id)
{
    m_dataManager.endSession(id);
    m_clients.remove(id);
}

void Server::onNetRecv(int net_id,QJsonObject obj)
{
    qDebug() << "onNetRecv" << obj;

    auto c = m_clients[net_id].data();
    QString method = obj["method"].toString();
    if(method == "newSession")
    {
        c->clear();
        c->session = obj["session"].toInt();
    }
    else if(method == "sendText")
    {
        QString text = obj["text"].toString();
        c->chatId = obj["chatId"].toInt();
        sendOllamaReq(net_id,text);
    }    
}

void Server::dealOllamaPack(QNetworkReply *reply)
{                    
    QByteArray buffer = reply->readAll();
    int net_id = reply->property("net_id").toInt();
    int session_id = reply->property("session_id").toInt();
    int chat_id = reply->property("chat_id").toInt();
    auto c = m_clients[net_id].data();
    if(session_id != c->session || c->chatId != chat_id || c->ollamaRecv >= 1024 * 1024)        
    {
        c->closeOllamaReply();
        return;
    }

    QString recvText;
    m_dataManager.recvData(net_id,buffer);
    while(m_dataManager.packCount(net_id) > 0)
    {
        QJsonObject pack = m_dataManager.takePack(net_id);
        QString res = pack["response"].toString();        
        if (!res.isEmpty())        
            recvText.append(res);                    
    }

    if(!recvText.isEmpty())
    {
        QJsonObject obj;
        obj["session"] = c->session;
        obj["chatId"] = c->chatId;
        obj["method"] = "recvText";
        obj["text"] = recvText;        
        sendPack(net_id,obj);
    }
}

void Server::onTextReply()
{
    QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
    int net_id = reply->property("net_id").toInt();
    if(!m_server.hasConnect(net_id))
        return;

    dealOllamaPack(reply);
}

void Server::onTextReplyFinish()
{
    QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
    int net_id = reply->property("net_id").toInt();
    if(!m_clients.contains(net_id))
        return;
         
    auto c = m_clients[net_id].data();
    if(reply->error() != QNetworkReply::NoError)
    {
        qDebug() << "error:" << reply->errorString();
        return;
    }

    dealOllamaPack(reply);   
    QJsonObject obj;
    obj["session"] = c->session;
    obj["chatId"] = c->chatId;
    obj["method"] = "recvTextFinish";            
    sendPack(net_id,obj);
}

void Server::sendPack(int net_id,const QJsonObject &obj)
{
    m_server.sendPack(net_id,obj);
}

void Server::sendOllamaReq(int net_id,QString text)
{
    auto c = m_clients[net_id].data();
    c->closeOllamaReply();

    QUrl url("http://localhost:11434/api/generate");
    QUrlQuery query;
    url.setQuery(query);

    QJsonObject obj;
    obj["model"] = "deepseek-r1:7b";
    obj["prompt"] = text;    
    obj["stream"] = true;

    QByteArray json = QJsonDocument(obj).toJson(QJsonDocument::Compact);

    QNetworkRequest req;
    req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");    
    req.setUrl(url);

    QNetworkReply *reply = m_manager->post(req,json);
    reply->setProperty("net_id",net_id);
    reply->setProperty("session_id",c->session);
    reply->setProperty("chat_id",c->chatId);
    connect(reply, &QNetworkReply::readyRead, this, &Server::onTextReply);
    connect(reply, &QNetworkReply::finished, this, &Server::onTextReplyFinish);

    c->ollamaReply = reply;
}